import random
from typing import List, Optional
from scrapy import signals
from scrapy.exceptions import NotConfigured
import structlog


class UserAgentMiddleware:
    
    DESKTOP_AGENTS = [
        # Chrome
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        
        # Firefox
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0",
        "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0",
        "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0",
        
        # Safari
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15",
        
        # Edge
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0",
        
        # Opera
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 OPR/106.0.0.0",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 OPR/106.0.0.0",
    ]
    
    MOBILE_AGENTS = [
        # iOS Safari
        "Mozilla/5.0 (iPhone; CPU iPhone OS 17_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1",
        "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
        "Mozilla/5.0 (iPad; CPU OS 17_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        
        # Android Chrome
        "Mozilla/5.0 (Linux; Android 13; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
        "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
        "Mozilla/5.0 (Linux; Android 12; SM-S906N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36",
        
        # Android Firefox
        "Mozilla/5.0 (Android 13; Mobile; rv:121.0) Gecko/121.0 Firefox/121.0",
        "Mozilla/5.0 (Android 12; Mobile; rv:121.0) Gecko/121.0 Firefox/121.0",
        
        # Samsung Browser
        "Mozilla/5.0 (Linux; Android 13; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36",
        
        # WeChat Browser
        "Mozilla/5.0 (Linux; Android 12; HarmonyOS; NOH-AN01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 Mobile Safari/537.36 MicroMessenger/8.0.32",
        "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.43",
    ]
    
    def __init__(self, user_agent_list: Optional[List[str]] = None, device_type: str = "mixed"):
        self.logger = structlog.get_logger(self.__class__.__name__)
        self.device_type = device_type.lower()
        
        if user_agent_list:
            self.user_agents = user_agent_list
        else:
            self.user_agents = self._get_default_agents()
        
        self.usage_stats = {}
        for ua in self.user_agents:
            self.usage_stats[ua] = 0
        
        self.logger.info(
            "UserAgent middleware initialized",
            total_agents=len(self.user_agents),
            device_type=self.device_type
        )
    
    @classmethod
    def from_crawler(cls, crawler):
        user_agent_list = crawler.settings.getlist('USER_AGENT_LIST', [])
        
        device_type = crawler.settings.get('USER_AGENT_TYPE', 'mixed')
        
        instance = cls(user_agent_list, device_type)
        
        crawler.signals.connect(instance.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(instance.spider_closed, signal=signals.spider_closed)
        
        return instance
    
    def _get_default_agents(self) -> List[str]:
        if self.device_type == "desktop":
            return self.DESKTOP_AGENTS.copy()
        elif self.device_type == "mobile":
            return self.MOBILE_AGENTS.copy()
        else:  # mixed
            return self.DESKTOP_AGENTS.copy() + self.MOBILE_AGENTS.copy()
    
    def get_random_user_agent(self) -> str:
        least_used_agents = sorted(
            self.user_agents, 
            key=lambda ua: self.usage_stats.get(ua, 0)
        )
        
        candidates = least_used_agents[:max(1, len(least_used_agents) // 3)]
        
        selected_ua = random.choice(candidates)
        self.usage_stats[selected_ua] += 1
        
        return selected_ua
    
    def process_request(self, request, spider):
        if request.meta.get('dont_change_user_agent'):
            return None
        
        custom_ua = request.meta.get('user_agent')
        if custom_ua:
            request.headers['User-Agent'] = custom_ua
        else:
            user_agent = self.get_random_user_agent()
            request.headers['User-Agent'] = user_agent
            request.meta['assigned_user_agent'] = user_agent
        
        self.logger.debug(
            "User-Agent assigned",
            url=request.url,
            user_agent=request.headers.get('User-Agent', b'').decode('utf-8')
        )
    
    def spider_opened(self, spider):
        self.logger.info(
            "UserAgent middleware activated",
            spider=spider.name,
            total_user_agents=len(self.user_agents)
        )
    
    def spider_closed(self, spider):
        most_used = sorted(
            self.usage_stats.items(), 
            key=lambda x: x[1], 
            reverse=True
        )[:5]
        
        least_used = sorted(
            self.usage_stats.items(), 
            key=lambda x: x[1]
        )[:5]
        
        total_requests = sum(self.usage_stats.values())
        
        self.logger.info(
            "UserAgent middleware statistics",
            spider=spider.name,
            total_requests=total_requests,
            unique_agents_used=len([ua for ua, count in self.usage_stats.items() if count > 0]),
            most_used_agents=most_used,
            least_used_agents=least_used
        )